In [1]:
# Making a density map
# This Choropleth map shows the number of dentists by county. The labels on each county shape indicates the numercial sums of dentists. Even though the data used here is for BOD stand-alone part only, you can use the same code to make maps for Medicaid and CHIP. Here are the color schemes that I would like you to follow so that the color choices are uniform across all states:

# Medicaid - bluegreen
# CHIP - orangered
# Medicaid and CHIP combined - yellowgreen (for states that list Med and CHIP dentists in one category)
# BOD - bluepurple
In [2]:
import altair as alt
import pandas as pd
import geopandas as gpd
import json
In [3]:
alt.themes.enable('opaque')

pd.set_option('display.max_rows', 3200)
In [4]:
df_ruca = gpd.read_file(r"/Users/tinalu/Documents/ISyE Research/Shapes/Colorado RUCA Files")
df_ruca.columns
Out[4]:
Index(['STATEFP', 'COUNTYFP', 'TRACTCE', 'GEOID', 'NAME', 'NAMELSAD', 'MTFCC',
       'FUNCSTAT', 'ALAND', 'AWATER', 'INTPTLAT', 'INTPTLON', 'geometry'],
      dtype='object')
In [5]:
df_ruca.head(2)
Out[5]:
STATEFP COUNTYFP TRACTCE GEOID NAME NAMELSAD MTFCC FUNCSTAT ALAND AWATER INTPTLAT INTPTLON geometry
0 08 041 002300 08041002300 23 Census Tract 23 G5020 S 3164176 0 +38.8272733 -104.8263887 POLYGON ((-104.83629 38.82296, -104.83629 38.8...
1 08 041 006600 08041006600 66 Census Tract 66 G5020 S 2516898 91797 +38.8687188 -104.8744389 POLYGON ((-104.88195 38.85660, -104.88190 38.8...
In [6]:
# RUCA Private Insurance Map

#read in a CO chip file with sum of dentists in each tract
bod_ctdf = pd.read_csv('CO_BOD_census_tract.csv')
bod_ctdf.State_County_Tract_FIPS_Code = '0'+bod_ctdf.State_County_Tract_FIPS_Code.astype(str)
# bod_ctdf.State_County_Tract_FIPS_Code = bod_ctdf.State_County_Tract_FIPS_Code.astype(str)
df_ruca.GEOID = df_ruca.GEOID.astype(str)

# Merge the shapefiles with BOD file containing sums of dentists by tract
bod_ctdf2 = df_ruca.merge(bod_ctdf, left_on='GEOID', right_on = 'State_County_Tract_FIPS_Code', how='left')

#get the centroids so that labels of the number of dentists can be positioned there.
bod_ctdf2['centroid_lon'] = bod_ctdf2['geometry'].centroid.x
bod_ctdf2['centroid_lat'] = bod_ctdf2['geometry'].centroid.y

# convert the sums of dentists into strings so that they can be used as labels
bod_ctdf2['Count'] = bod_ctdf2['Count'].astype(str)

#convert data into json, since Altair expects the json data format.
choro_json = json.loads(bod_ctdf2.to_json())

#extract the part that Altair wants for mapping
choro_data = alt.Data(values=choro_json['features'])

#function to define how the map will be made.
def gen_map(geodata, color_column, title, tooltip):
    '''Generates map of counties with labels'''
    # Add Base Layer
    base = alt.Chart(geodata, title = title).mark_geoshape(
        fill='white',
        stroke='black',
        strokeWidth=1
    ).encode(
    ).properties(
        width=800,
        height=800
    )
    
    choro = alt.Chart(geodata).mark_geoshape(stroke='black').encode(
            color=alt.Color(color_column, 
                            legend=alt.Legend(title="Number of Dentists"), 
                            scale=alt.Scale(scheme='bluepurple')
            ), tooltip=[alt.Tooltip('properties.GEOID:O', title='Census Tract'),
                        alt.Tooltip('properties.Count:Q', title='Number of Dentists')]
            ).transform_lookup(
                lookup='properties',
                from_=alt.LookupData(geodata, 'properties', ['Count'])
            )
    
    # Add Labels Layer. Here they are the sums of dentists per census tract
    labels = alt.Chart(geodata).mark_text(baseline='top'
     ).properties(
        width=400,
        height=400
     ).encode(
         longitude='properties.centroid_lon:Q',
         latitude='properties.centroid_lat:Q',
         text='properties.Count:O',
         size=alt.value(10),
         opacity=alt.value(1)
     )
    
    return base + choro #use this line if you don't want the text labels   
#     return base + choro + labels #use this line if you want the text labels

#Call function, pass the appropriate parameters.
CO_private_ruca_map = gen_map(geodata=choro_data, color_column='properties.Count:Q', 
            title='Colorado Number of Dentists per Census Tract: Accepting Private Insurance Only', # TODO: Change state
            tooltip=['properties.GEOID:O','properties.Count:Q'])

CO_private_ruca_map
Out[6]:
In [ ]:
# RUCA Medicaid Map

#read in a CO chip file with sum of dentists in each tract
med_ctdf = pd.read_csv('CO_med_census_tract.csv')

med_ctdf.State_County_Tract_FIPS_Code = '0'+med_ctdf.State_County_Tract_FIPS_Code.astype(str)
# med_ctdf.State_County_Tract_FIPS_Code = med_ctdf.State_County_Tract_FIPS_Code.astype(str)
df_ruca.GEOID = df_ruca.GEOID.astype(str)

# Merge the shapefiles with BOD file containing sums of dentists by county.
med_ctdf2 = df_ruca.merge(med_ctdf, left_on='GEOID', right_on = 'State_County_Tract_FIPS_Code', how='left')

#get the centroids so that labels of the number of dentists can be positioned there.
med_ctdf2['centroid_lon'] = med_ctdf2['geometry'].centroid.x
med_ctdf2['centroid_lat'] = med_ctdf2['geometry'].centroid.y

# convert the sums of dentists into strings so that they can be used as labels
med_ctdf2['Count'] = med_ctdf2['Count'].astype(str)

#convert data into json, since Altair expects the json data format.
choro_json = json.loads(med_ctdf2.to_json())

#extract the part that Altair wants for mapping
choro_data = alt.Data(values=choro_json['features'])


#function to define how the map will be made.
def gen_map(geodata, color_column, title, tooltip):
    '''Generates map of tracts with labels'''
    # Add Base Layer
    base = alt.Chart(geodata, title = title).mark_geoshape(
        fill='white',
        stroke='black',
        strokeWidth=1
    ).encode(
    ).properties(
        width=800,
        height=800
    )
    
    choro = alt.Chart(geodata).mark_geoshape(stroke='black').encode(
            color=alt.Color(color_column, 
                            legend=alt.Legend(title="Number of Dentists"), 
                            scale=alt.Scale(scheme='bluegreen')
            ), tooltip=[alt.Tooltip('properties.GEOID:O', title='Census Tract'),
                        alt.Tooltip('properties.Count:Q', title='Number of Dentists')]
            ).transform_lookup(
                lookup='properties',
                from_=alt.LookupData(geodata, 'properties', ['Count'])
            )
    
    # Add Labels Layer. Here they are the sums of dentists per census tract
    labels = alt.Chart(geodata).mark_text(baseline='top'
     ).properties(
        width=400,
        height=400
     ).encode(
         longitude='properties.centroid_lon:Q',
         latitude='properties.centroid_lat:Q',
         text='properties.Count:O',
         size=alt.value(10),
         opacity=alt.value(1)
     )
    
    return base + choro #use this line if you don't want the text labels   
#     return base + choro + labels #use this line if you want the text labels

#Call function, pass the appropriate parameters.
CO_medicaid_ruca_map = gen_map(geodata=choro_data, color_column='properties.Count:Q', 
            title='Colorado Number of Dentists per Census Tract: Accepting Medicaid Only', # TODO: Change state
            tooltip=['properties.GEOID:O','properties.Count:Q'])

CO_medicaid_ruca_map
In [ ]:
# RUCA CHIP Map

#read in a CO chip file with sum of dentists in each tract
chip_ctdf = pd.read_csv('CO_chip_census_tract.csv')

chip_ctdf.State_County_Tract_FIPS_Code = '0'+chip_ctdf.State_County_Tract_FIPS_Code.astype(str)
# chip_ctdf.State_County_Tract_FIPS_Code = chip_ctdf.State_County_Tract_FIPS_Code.astype(str)
df_ruca.GEOID = df_ruca.GEOID.astype(str)

# Merge the shapefiles with BOD file containing sums of dentists by tract
chip_ctdf2 = df_ruca.merge(chip_ctdf, left_on='GEOID', right_on = 'State_County_Tract_FIPS_Code', how='left')

#get the centroids so that labels of the number of dentists can be positioned there.
chip_ctdf2['centroid_lon'] = chip_ctdf2['geometry'].centroid.x
chip_ctdf2['centroid_lat'] = chip_ctdf2['geometry'].centroid.y

# convert the sums of dentists into strings so that they can be used as labels
chip_ctdf2['Count'] = chip_ctdf2['Count'].astype(str)

# Convert data into json, since Altair expects the json data format.
choro_json = json.loads(chip_ctdf2.to_json())

#extract the part that Altair wants for mapping
choro_data = alt.Data(values=choro_json['features'])


#function to define how the map will be made.
def gen_map(geodata, color_column, title, tooltip):
    '''Generates map of tracts with labels'''
    # Add Base Layer
    base = alt.Chart(geodata, title = title).mark_geoshape(
        fill='white',
        stroke='black',
        strokeWidth=1
    ).encode(
    ).properties(
        width=800,
        height=800
    )
    
    choro = alt.Chart(geodata).mark_geoshape(stroke='black').encode(
            color=alt.Color(color_column, 
                            legend=alt.Legend(title="Number of Dentists"), 
                            scale=alt.Scale(scheme='orangered')
            ), tooltip=[alt.Tooltip('properties.GEOID:O', title='Census Tract'),
                        alt.Tooltip('properties.Count:Q', title='Number of Dentists')]
            ).transform_lookup(
                lookup='properties',
                from_=alt.LookupData(geodata, 'properties', ['Count'])
            )
    
    # Add Labels Layer. Here they are the sums of dentists per census tract
    labels = alt.Chart(geodata).mark_text(baseline='top'
     ).properties(
        width=400,
        height=400
     ).encode(
         longitude='properties.centroid_lon:Q',
         latitude='properties.centroid_lat:Q',
         text='properties.Count:O',
         size=alt.value(10),
         opacity=alt.value(1)
     )
    
    return base + choro #use this line if you don't want the text labels   
#     return base + choro + labels #use this line if you want the text labels

#Call function, pass the appropriate parameters.
CO_chip_ruca_map = gen_map(geodata=choro_data, color_column='properties.Count:Q', 
            title='Colorado Number of Dentists per Census Tract: Accepting CHIP Only', # TODO: Change state
            tooltip=['properties.GEOID:O','properties.Count:Q'])

CO_chip_ruca_map